#pragma once

#include "Vector.h"

/**
 * <p>Title: CQuat</p>
 * <p>Description: Quaternion class</p>
 * <p>Copyright: Copyright (c) 2003</p>
 * <p>Company: CIANT</p>
 * @author not attributable
 * @version 1.0
 */

/** Quaternion class. Quaternions are often used in computer graphics (and associated geometric analysis) to represent rotations and orientations of objects in three-dimensional space [Wikipedia].
*
*/
class __declspec(dllexport) CQuat {

protected:
  /// Used in getEuler. When there is singularity at south/north pole, roll will be zero.
  static const float singularityTreshold;
  /// Used in setRotationAxis. When the magnitude of the vector is smaller then the treshold, resulting quaternion will be [0 0 0 1].
  static const float vectorSizeTreshold;
  /// Used in getRotation. When the magnitude of the quaternion is smaller then the treshold, resulting rotation will be zero around [0 0 1] axis.
  static const float zeroRotationTreshold;

public:

  /// X component of the quaternion's vector 
  float x;
  /// Y component of the quaternion's vector 
  float y;
  /// Z component of the quaternion's vector 
  float z;
  /// W component 
  float w;

  /// Construct zero rotation quaternion [0 0 0 1].
  CQuat(): x(0), y(0), z(0), w(1) {
  }

  /// Construct a quaternion given its 4 components.
  CQuat(float arr[]) {
    x = arr[0]; y = arr[1]; z = arr[2]; w = arr[3];
  }

  /// Construct a quaternion given its 4 components.
  CQuat(float xx, float yy, float zz, float ww) {
    x = xx;
    y = yy;
    z = zz;
    w = ww;
  }

  /** Construct a quaternion given Euler angles.
  * @param e euler angles: az, el, ro in degrees(!)
  */
  void setEuler(float e[]);

  /// Transform the quaternion to represent the same rotation in the reverse direction.
  void conjugate() {
    x = -x; y = -y; z = -z;
  }

  /// Normalize the quaternion.
  void normalize();

  /** Rotate vector by the quaternion.
  * @param v the vector to rotate
  * @param dest resulting rotated vector
  */
  void multiply(float v[3], float dest[3]);

  /** Calculate difference between two quaternions.
  * @param qa first quaternion
  * @param qb second quaternion
  * @return difference angle in radians
  */
  static float diff(CQuat qa, CQuat qb);

  /** Compute spherical linear interpolation between two quaternions.
  * @param qa first quaternion
  * @param qb second quaternion
  * @param t interpolation parametr (0..1)
  * @return interpolated quaternion
  */
  static CQuat slerp(CQuat qa, CQuat qb, float t);

  /** Convert the quaternion to Euler angles.
  * @param e resulting array of az, el, ro
  */
  void getEuler(float e[]);

  /** Set the quaternion given an axis and rotation.
  * @param v[0] x
  * @param v[1] y
  * @param v[2] z
  * @param v[3] angle in radians
  */
  void setRotationAxis(float v[]);

  /** Multiply two quaternions A (this quaternion) and B.
  * @param B B quaternion
  * @return quaternion A * B
  */
  CQuat multiply(CQuat B);

  /** Convert the quaternion to a rotation around an axis.
  * @param v resulting axis
  * @return angle in radians
  */
  float getRotation(CVector3D* v);

  /** Get the 4 quaternion components.
  * @param arr resulting array
  */
  void toFloat(float arr[]);

protected:
  CQuat multiplyAux(float v[3]);

};
